﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Ckknight.StateMachine
{
    /// <summary>
    /// Internal dictionary-like class that cannot be changed after instantiation.
    /// 
    /// Does not use bucketing like the normal System.Collections.Generic.Dictionary`2, but rather iterates over an array.
    /// 
    /// Assuming a state machine does not have an excessive amount of states and/or triggers, that should be plenty efficient.
    /// </summary>
    internal sealed class FrozenDictionary<TKey, TValue> : IDictionary<TKey, TValue>
    {
        internal FrozenDictionary(IEnumerable<KeyValuePair<TKey, TValue>> pairs, IEqualityComparer<TKey> comparer)
        {
            if (pairs == null)
            {
                throw new ArgumentNullException("pairs");
            }

            if (comparer == null)
            {
                comparer = EqualityComparer<TKey>.Default;
            }

            var collection = pairs as ICollection<KeyValuePair<TKey, TValue>> ?? pairs.ToArray();

            _comparer = comparer;
            _count = collection.Count;
            _keys = new TKey[_count];
            _values = new TValue[_count];

            int i = 0;
            foreach (var pair in collection)
            {
                _keys[i] = pair.Key;
                _values[i] = pair.Value;
                i++;
            }
        }

        private readonly IEqualityComparer<TKey> _comparer;
        private readonly int _count;
        private readonly TKey[] _keys;
        private readonly TValue[] _values;

        #region IDictionary<TKey,TValue> Members

        void IDictionary<TKey,TValue>.Add(TKey key, TValue value)
        {
            throw new NotSupportedException();
        }

        public bool ContainsKey(TKey key)
        {
            for (int i = 0; i < _count; i++)
            {
                if (_comparer.Equals(key, _keys[i]))
                {
                    return true;
                }
            }
            return false;
        }

        public KeyCollection Keys
        {
            get
            {
                return new KeyCollection(_keys, _comparer);
            }
        }

        ICollection<TKey> IDictionary<TKey, TValue>.Keys
        {
            get
            {
                return this.Keys;
            }
        }

        bool IDictionary<TKey, TValue>.Remove(TKey key)
        {
            throw new NotSupportedException();
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            for (int i = 0; i < _count; i++)
            {
                if (_comparer.Equals(key, _keys[i]))
                {
                    value = _values[i];
                    return true;
                }
            }
            value = default(TValue);
            return false;
        }

        public ValueCollection Values
        {
            get
            {
                return new ValueCollection(_values);
            }
        }

        ICollection<TValue> IDictionary<TKey, TValue>.Values
        {
            get
            {
                return this.Values;
            }
        }

        public TValue this[TKey key]
        {
            get
            {
                TValue value;
                if (!TryGetValue(key, out value))
                {
                    throw new KeyNotFoundException();
                }
                return value;
            }
        }

        TValue IDictionary<TKey, TValue>.this[TKey key]
        {
            get
            {
                return this[key];
            }
            set
            {
                throw new NotSupportedException();
            }
        }

        #endregion

        #region ICollection<KeyValuePair<TKey,TValue>> Members

        void ICollection<KeyValuePair<TKey,TValue>>.Add(KeyValuePair<TKey, TValue> item)
        {
            throw new NotSupportedException();
        }

        void ICollection<KeyValuePair<TKey, TValue>>.Clear()
        {
            throw new NotSupportedException();
        }

        bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
        {
            TValue currentValue;
            return TryGetValue(item.Key, out currentValue) && object.Equals(item.Value, currentValue);
        }

        void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            for (int i = 0; i < _count; i++)
            {
                array[arrayIndex++] = new KeyValuePair<TKey, TValue>(_keys[i], _values[i]);
            }
        }

        public int Count
        {
            get
            {
                return _count;
            }
        }

        bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
        {
            get
            {
                return true;
            }
        }

        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
        {
            throw new NotSupportedException();
        }

        #endregion

        public Enumerator GetEnumerator()
        {
            return new Enumerator(this);
        }

        #region IEnumerable<KeyValuePair<TKey,TValue>> Members

        IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        #endregion

        public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>
        {
            public Enumerator(FrozenDictionary<TKey, TValue> dict)
            {
                _index = -1;
                _count = dict._count;
                _keys = dict._keys;
                _values = dict._values;
            }

            private readonly int _count;
            private readonly TKey[] _keys;
            private readonly TValue[] _values;

            private int _index;

            #region IEnumerator<KeyValuePair<TKey,TValue>> Members

            public KeyValuePair<TKey, TValue> Current
            {
                get
                {
                    return new KeyValuePair<TKey, TValue>(_keys[_index], _values[_index]);
                }
            }

            #endregion

            #region IDisposable Members

            public void Dispose()
            {
            }

            #endregion

            #region IEnumerator Members

            object System.Collections.IEnumerator.Current
            {
                get
                {
                    return this.Current;
                }
            }

            public bool MoveNext()
            {
                _index++;
                return _index < _count;
            }

            public void Reset()
            {
                _index = -1;
            }

            #endregion
        }

        public struct KeyCollection : ICollection<TKey>
        {
            #region Private fields

            private readonly TKey[] _keys;
            private readonly IEqualityComparer<TKey> _comparer;

            #endregion

            #region Constructor

            internal KeyCollection(TKey[] keys, IEqualityComparer<TKey> comparer)
            {
                _keys = keys;
                _comparer = comparer;
            }

            #endregion

            #region ICollection<TKey> Members

            void ICollection<TKey>.Add(TKey item)
            {
                throw new NotSupportedException();
            }

            void ICollection<TKey>.Clear()
            {
                throw new NotSupportedException();
            }

            public bool Contains(TKey item)
            {
                return _keys.Contains(item, _comparer);
            }

            void ICollection<TKey>.CopyTo(TKey[] array, int arrayIndex)
            {
                Array.Copy(_keys, 0, array, arrayIndex, _keys.Length);
            }

            public int Count
            {
                get
                {
                    return _keys.Length;
                }
            }

            bool ICollection<TKey>.IsReadOnly
            {
                get
                {
                    return true;
                }
            }

            bool ICollection<TKey>.Remove(TKey item)
            {
                throw new NotSupportedException();
            }

            #endregion

            #region IEnumerable<TKey> Members

            public IEnumerator<TKey> GetEnumerator()
            {
                return ((IEnumerable<TKey>)_keys).GetEnumerator();
            }

            #endregion

            #region IEnumerable Members

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return ((System.Collections.IEnumerable)_keys).GetEnumerator();
            }

            #endregion
        }

        public struct ValueCollection : ICollection<TValue>
        {
            #region Private fields

            private readonly TValue[] _values;

            #endregion

            #region Constructor

            internal ValueCollection(TValue[] values)
            {
                _values = values;
            }

            #endregion

            #region ICollection<TValue> Members

            void ICollection<TValue>.Add(TValue item)
            {
                throw new NotSupportedException();
            }

            void ICollection<TValue>.Clear()
            {
                throw new NotSupportedException();
            }

            public bool Contains(TValue item)
            {
                return _values.Contains(item);
            }

            void ICollection<TValue>.CopyTo(TValue[] array, int arrayIndex)
            {
                Array.Copy(_values, 0, array, arrayIndex, _values.Length);
            }

            public int Count
            {
                get
                {
                    return _values.Length;
                }
            }

            bool ICollection<TValue>.IsReadOnly
            {
                get
                {
                    return true;
                }
            }

            bool ICollection<TValue>.Remove(TValue item)
            {
                throw new NotSupportedException();
            }

            #endregion

            #region IEnumerable<TValue> Members

            public IEnumerator<TValue> GetEnumerator()
            {
                return ((IEnumerable<TValue>)_values).GetEnumerator();
            }

            #endregion

            #region IEnumerable Members

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return ((System.Collections.IEnumerable)_values).GetEnumerator();
            }

            #endregion
        }
    }
}
